﻿using System;
using System.Windows;
using System.IO;
using System.Threading;
using System.Windows.Threading;

namespace ECLogger
{
    public class Logger
    {
        public enum LOG_TYPE
        {
            COMPONENTS,
            FILE,
            BOTH
        }

        public enum LOG_LEVEL
        {
            DEBUG,
            INFO,
            NOTICE,
            WARNING,
            ERROR
        }

        private struct LOG_CONTENT
        {
            public string msg;
            public LOG_LEVEL level;
        }

        private LOG_TYPE type;
        private System.Windows.Controls.TextBox comp;
        private LOG_LEVEL logLevel = LOG_LEVEL.INFO;
        private string logfile;
        private bool autoScroll = true;
        private int maxLines = 50;
        private int logLines = 0;

        public delegate void dlg();

        public Logger() { }

        ~Logger() { }

        public void Init(System.Windows.Controls.TextBox compName)
        {
            type = LOG_TYPE.COMPONENTS;
            comp = compName;
        }

        public void Init(string logFile)
        {
            type = LOG_TYPE.FILE;
            logfile = logFile;
        }

        public void Init(string logFile, System.Windows.Controls.TextBox compName)
        {
            try
            {
                type = LOG_TYPE.BOTH;
                comp = compName;
                logfile = logFile;
            }
            catch (Exception ex)
            {
                type = LOG_TYPE.COMPONENTS;
                Error("Open FileStream Failed.\r\n" + ex.ToString());
            }
        }

        public void setLogLevel(LOG_LEVEL level)
        {
            logLevel = level;
        }

        public void setAutoScroll(bool isAutoScroll = true)
        {
            autoScroll = isAutoScroll;
        }

        public void setMaxDisplayLines(int lines)
        {
            maxLines = lines;
        }

        public void Debug(string msg, params object[] args)
        {
            msg = string.Format(msg, args);
            writeLogThread(msg, LOG_LEVEL.DEBUG);
        }

        public void Info(string msg, params object[] args )
        {
            msg = string.Format(msg, args);
            writeLogThread(msg, LOG_LEVEL.INFO);
        }

        public void Notice(string msg, params object[] args)
        {
            msg = string.Format(msg, args);
            writeLogThread(msg, LOG_LEVEL.NOTICE);
        }
        public void Warning(string msg, params object[] args)
        {
            msg = string.Format(msg, args);
            writeLogThread(msg, LOG_LEVEL.WARNING);
        }
        public void Error(string msg, params object[] args)
        {
            msg = string.Format(msg, args);
            writeLogThread(msg, LOG_LEVEL.ERROR);
        }

        private void writeLogThread(string msg, LOG_LEVEL level)
        {
            LOG_CONTENT lc;
            lc.msg = msg;
            lc.level = level;
            new Thread(new ParameterizedThreadStart(writeLog)).Start(lc);
        }

        private void writeLog(object log_content)
        {
            string msg = ((LOG_CONTENT)log_content).msg;
            LOG_LEVEL level = ((LOG_CONTENT)log_content).level;
            if (level < logLevel) return;
            string[] loglvl = { "DEBUG", "INFO", "NOTICE", "WARNING", "ERROR" };
            string logmsg = string.Format("[{0}]{1} - {2}\r\n",
                loglvl[(int)level],
                DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"),
                msg);
            void toComp()
            {
                void act()
                {
                    logLines++;
                    if (logLines > maxLines)
                    {
                        comp.Text = comp.Text.Remove(0, comp.Text.IndexOf("\r\n") + 2);
                        logLines--;
                    }
                    comp.Text += logmsg;
                    if (autoScroll) comp.ScrollToEnd();
                }
                comp.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new dlg(act));
            }
            void toFile()
            {
                using (var fs = new FileStream(logfile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 1024))
                {
                    byte[] bstr = System.Text.Encoding.Default.GetBytes(logmsg);
                    fs.Write(bstr, 0, bstr.Length);
                    fs.Close();
                    fs.Dispose();
                }
            }
            switch (type)
            {
                case LOG_TYPE.BOTH:
                    toComp();
                    toFile();
                    break;
                case LOG_TYPE.COMPONENTS:
                    new Thread(new ThreadStart(toComp));
                    toComp();
                    break;
                case LOG_TYPE.FILE:
                    new Thread(new ThreadStart(toFile));
                    toFile();
                    break;
                default: break;
            }
        }
    }
}
